package utils

import (
	"flag"
	"fmt"
	"log"
	"strconv"
	"time"

	"github.com/google/uuid"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"go.uber.org/zap"
)

/*
 +----------------------------------------------------------------------
 + Title        : 初始化项目
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2020-01-06
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : 初始化项目
 +----------------------------------------------------------------------
*/

// 运行环境
var UserSrvEnv string

// Bug模式
var BugMode string

// 服务名称
var ServiceName string

// 服务版本
var ServiceVersion string

// 服务地址
var ServiceHost string

// 服务端口
var ServicePort int

// http服务地址
var HttpHost string

// http服务端口
var HttpPort int

// ++++++++++++++++++++++ 服务注册与发现、配置中心开始 ++++++++++++++++++
// 服务发现
var ServiceDiscovery string

// 服务注册
var ServiceRegister string

// 配置中心
var ConfigCenter string

// nacos 客户配置
var NacosClientConfig constant.ClientConfig

// nacos 服务配置
var NacosServerConfigs []constant.ServerConfig

// nacos group
var nacosGroup string

// consul服务
var ConsulService *ConsulServiceRegister

// +++++++++++++++++ 服务注册与发现、配置中心结束 +++++++++++++

// +++++++++++++++++++++ Zap 日志相关参数开始 +++++++++++++++++++++
var ZapLoggerMaxSize int
var ZapLoggerMaxBackups int
var ZapLoggerMaxAge int
var ZapLoggerCompress bool
var ZapLog *zap.Logger

// +++++++++++++++++++++ Zap 日志相关参数结束 +++++++++++++++++++++

// ++++++++++++++++++++++++++ jaeger 开始 +++++++++++++++++++++++++
var JaegerAddr string

// ++++++++++++++++++++++++++ jaeger 结束 +++++++++++++++++++++++++

// 配置中心 mysql 名称
var MySqlConfigCenter string

// 配置中心 redis 名称
var RedisConfigCenter string

// 配置中心 mongo 名称
var MongoConfigCenter string

func init() {
	fmt.Println("----------- user service app int -----------")
	// 设置日志标志：添加 Llongfile 或 Lshortfile
	log.SetFlags(log.LstdFlags | log.Llongfile) // 完整路径
	// 或者
	// log.SetFlags(log.LstdFlags | log.Lshortfile) // 短路径

	appConfig, err := GetConfig("app", "yml")
	if err != nil {
		log.Fatalf("Failed to read app config: %v", err)
	}

	// ------------------ zaplogger 开始 ------------------
	ZapLoggerMaxSize = appConfig.GetInt(UserSrvEnv + ".zlogger_maxsize")
	ZapLoggerMaxBackups = appConfig.GetInt(UserSrvEnv + ".zlogger_max_backups")
	ZapLoggerMaxAge = appConfig.GetInt(UserSrvEnv + ".zlogger_max_age")
	ZapLoggerCompress = appConfig.GetBool(UserSrvEnv + ".zlogger_compress")
	// ------------------ zaplogger 结束 ------------------

	// 运行环境
	flag.StringVar(&UserSrvEnv, "UserSrvEnv", "dev", "运行环境")
	flag.Parse()

	log.Println("current UserSrvEnvironment: ", UserSrvEnv)

	// Bug模式
	BugMode = appConfig.GetString("bug_mode")

	// 获取服务名称
	ServiceName = appConfig.GetString("service_name")

	// 获取服务版本
	ServiceVersion = appConfig.GetString("service_version")

	// 获取服务地址
	ServiceHost = appConfig.GetString(UserSrvEnv + ".service_host")

	// 获取服务端口
	ServicePort = appConfig.GetInt(UserSrvEnv + ".service_port")

	// 获取http服务地址
	HttpHost = appConfig.GetString(UserSrvEnv + ".http_host")

	// 获取http服务端口
	HttpPort = appConfig.GetInt(UserSrvEnv + ".http_port")

	// 服务注册
	ServiceRegister = appConfig.GetString(UserSrvEnv + ".service_register")

	// 服务发现
	ServiceDiscovery = appConfig.GetString(UserSrvEnv + ".service_discovery")

	// 配置中心
	ConfigCenter = appConfig.GetString(UserSrvEnv + ".config_center")

	// 配置中心 mysql 名称
	MySqlConfigCenter = appConfig.GetString("mysql_config_center")

	// 配置中心 redis 名称
	RedisConfigCenter = appConfig.GetString("redis_config_center")

	// 配置中心 mongo 名称
	MongoConfigCenter = appConfig.GetString("mongo_config_center")

	// ------------------- 服务注册开始 -------------------
	InitServiceRegister()
	// ------------------- 服务注册结束 -------------------

	// JaegerAddr
	JaegerAddr = appConfig.GetString(UserSrvEnv + ".jaeger_addr")

	// proto3字段值为空时被忽略问题
	RemoveProtoJsonOmitempty("./proto/user/user.pb.go")
}

/**
 * 初始化配置中心
 *
 * @param string configName -----------------------------------必传
 * @param any ConfigStruct ------------------------------------必传
 * @author huwl
 */
func InitConfigCenter(configName string, ConfigStruct any) {
	switch ConfigCenter {
	case "nacos":
		// 创建NACOS配置中心
		cc, err := NewNacosConfigCenter()
		if err != nil {
			log.Fatalf("Failed to create NacosConfigCenter: %v", err)
		}

		// 获取配置数据
		cc.GetConfigData(configName, ConfigStruct, nacosGroup)

	case "consul":
		// 读取CONSUL配置文件
		consulConf, err := GetConfig("consul", "yml")
		if err != nil {
			log.Fatalf("Failed to read consul config: %v", err)
		}

		// 创建CONSUL配置中心
		// 获取配置信息：key格式UserSrvEnv+"/"+configName => dev/mysql_config
		cc, err := NewConsulConfigCenter(
			consulConf.GetString(UserSrvEnv+".addrs"),
			consulConf.GetString(UserSrvEnv+".token"),
			consulConf.GetString(UserSrvEnv+".prefix")+"/"+configName,
		)
		if err != nil {
			log.Fatalf("Failed to read consul config: %v", err)
		}

		// 获取ETCD配置数据
		cc.GetConfigData(ConfigStruct)

	case "etcd":
		// 读取ETCD配置文件
		etcdConfig, err := GetConfig("etcd", "yml")
		if err != nil {
			log.Fatalf("Failed to read etcd config: %v", err)
		}

		// 创建ETCD配置中心
		cc, err := NewEtcdConfigCenter(
			etcdConfig.GetStringSlice(UserSrvEnv+".addrs"),
			etcdConfig.GetString(UserSrvEnv+".username"),
			etcdConfig.GetString(UserSrvEnv+".password"),
			etcdConfig.GetString(UserSrvEnv+".config_prefix")+"/"+configName,
		)
		if err != nil {
			log.Fatalf("Failed to create EtcdConfigCenter: %v", err)
		}

		// 获取ETCD配置数据
		cc.GetConfigData(ConfigStruct)

		// 监控ETCD配置
		cc.WatchConfig(configName, ConfigStruct)
	}
}

/**
 * 初始化服务注册
 *
 * @author huwl
 */
func InitServiceRegister() {
	switch ServiceRegister {
	case "nacos":
		// 读取nacos配置文件
		cfg, err := GetConfig("nacos", "yml")
		if err != nil {
			log.Fatalf("Failed to read nacos config: %v", err)
		}

		// 创建clientConfig

		// TimeoutMs            uint64 // 请求Nacos服务端的超时时间，默认是10000ms
		// NamespaceId          string // ACM的命名空间Id
		// Endpoint             string // 当使用ACM时，需要该配置. https://help.aliyun.com/document_detail/130146.html
		// RegionId             string // ACM&KMS的regionId，用于配置中心的鉴权
		// AccessKey            string // ACM&KMS的AccessKey，用于配置中心的鉴权
		// SecretKey            string // ACM&KMS的SecretKey，用于配置中心的鉴权
		// OpenKMS              bool   // 是否开启kms，默认不开启，kms可以参考文档 https://help.aliyun.com/product/28933.html
		//                             // 同时DataId必须以"cipher-"作为前缀才会启动加解密逻辑
		// CacheDir             string // 缓存service信息的目录，默认是当前运行目录
		// UpdateThreadNum      int    // 监听service变化的并发数，默认20
		// NotLoadCacheAtStart  bool   // 在启动的时候不读取缓存在CacheDir的service信息
		// UpdateCacheWhenEmpty bool   // 当service返回的实例列表为空时，不更新缓存，用于推空保护
		// Username             string // Nacos服务端的API鉴权Username
		// Password             string // Nacos服务端的API鉴权Password
		// LogDir               string // 日志存储路径
		// RotateTime           string // 日志轮转周期，比如：30m, 1h, 24h, 默认是24h
		// MaxAge               int64  // 日志最大文件数，默认3
		// LogLevel             string // 日志默认级别，值必须是：debug,info,warn,error，默认值是info
		NacosClientConfig = constant.ClientConfig{
			NamespaceId: cfg.GetString(UserSrvEnv + ".name_space_id"),
			Username:    cfg.GetString(UserSrvEnv + ".username"),
			Password:    cfg.GetString(UserSrvEnv + ".password"),
			// TimeoutMs:           cfg.GetUint64(UserSrvEnv + ".timeout_ms"),
			// NotLoadCacheAtStart: cfg.GetBool(UserSrvEnv + ".not_load_cache_at_start"),
			// LogDir:              cfg.GetString(UserSrvEnv + ".not_load_cache_at_start"),
			// CacheDir:            cfg.GetString(UserSrvEnv + ".cache_dir"),
			// LogLevel: cfg.GetString(UserSrvEnv + ".log_level"),
		}

		// 创建nacos serverConfig
		addrs := StringSplit(cfg.GetString(UserSrvEnv+".addrs"), ",")
		for _, addr := range addrs {
			// ContextPath string // Nacos的ContextPath，默认/nacos，在2.0中不需要设置
			// IpAddr      string // Nacos的服务地址
			// Port        uint64 // Nacos的服务端口
			// Scheme      string // Nacos的服务地址前缀，默认http，在2.0中不需要设置
			// GrpcPort    uint64 // Nacos的 grpc 服务端口, 默认为 服务端口+1000, 不是必填
			ipAddr := StringSplit(addr, ":")[0]
			port, _ := strconv.ParseUint(StringSplit(addr, ":")[1], 10, 64)
			NacosServerConfigs = append(NacosServerConfigs, constant.ServerConfig{
				IpAddr:      ipAddr,
				ContextPath: cfg.GetString(UserSrvEnv + ".context_path"),
				Port:        port,
				Scheme:      "http",
			})
		}

		// 创建服务注册
		sr, err := NewNacosServiceRegister()
		if err != nil {
			log.Fatalf("Failed to create NacosServiceRegister: %v", err)
		}

		nacosGroup = cfg.GetString(UserSrvEnv + ".group")

		// 注册服务
		_, err = sr.RegisterService(
			ServiceHost,
			uint64(ServicePort),
			ServiceName,
			cfg.GetString(UserSrvEnv+".cluster_name"),
			nacosGroup)
		if err != nil {
			log.Fatalf("Failed to nacos RegisterService: %v", err)
		}
		log.Println("nacos service register success")

	case "consul":
		// 读取consul配置文件
		consulConf, err := GetConfig("consul", "yml")
		if err != nil {
			log.Fatalf("Failed to read consul config: %v", err)
		}

		// 创建服务注册
		ConsulService, err = NewConsulServiceRegister(
			consulConf.GetString(UserSrvEnv+".addrs"),
			consulConf.GetString(UserSrvEnv+".secret_id"),
		)
		if err != nil {
			log.Fatalf("Failed to create ServiceRegister: %v", err)
		}

		// 注册服务
		ConsulService.RegisterService(
			consulConf.GetStringSlice("tags"),
			ServiceName,
			ServiceHost,
			ServicePort,
		)
		log.Println("consul service register success")

	case "etcd":
		// 读取 etcd 配置信息
		etcdConfig, err := GetConfig("etcd", "yml")
		if err != nil {
			log.Fatalf("Failed to read etcd config: %v", err)
		}

		serviceId := uuid.New().String()

		// 拼接名称
		watchedKey := etcdConfig.GetString(UserSrvEnv+".service_prefix") + "/" + ServiceName + "/" + serviceId
		// 创建服务注册
		sr, err := NewEtcdServiceRegister(
			etcdConfig.GetStringSlice(UserSrvEnv+".addrs"),
			etcdConfig.GetString(UserSrvEnv+".username"),
			etcdConfig.GetString(UserSrvEnv+".password"),
			watchedKey,
		)
		if err != nil {
			log.Fatalf("Failed to create ServiceRegister: %v", err)
		}

		serviceIP, err := GetServiceIP()
		if err != nil {
			log.Fatalf("Failed to get container ip: %v", err)
		}

		sr.KeepAlive(10, ServiceRegisterInfo{
			ID:          serviceId,
			ServiceName: ServiceName,
			Address:     serviceIP + ":" + strconv.Itoa(ServicePort),
			Version:     ServiceVersion,
			Weight:      1,
			Status:      "up",
			LastUpdated: time.Now().Unix(),
		})
		log.Println("etcd service register success")
	}
}
